﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Xml.Serialization;
using Microsoft.WowAddonStudio.FrameXml.Serialization.Attributes;

namespace Microsoft.WowAddonStudio.FrameXml.Serialization
{
    /// <summary>
    /// Represents an anchor of a layout frame used to position the frame.
    /// </summary>
    [Serializable]
    [DesignerCategory("code")]
    [XmlType(AnonymousType = true, Namespace = "http://www.blizzard.com/wow/ui/")]
	[DebuggerDisplay("Point = {Point}, RelativePoint = {(RelativePointValueSpecified) ? RelativePoint : Point}, Offset = {(Dimension)Offset}")]
    public class Anchor : SerializationObject, ICloneable, ICopiable<Anchor>
    {
        private FramePoint? relativePoint;

        /// <summary>
        /// Gets or sets the offset of the anchor.
        /// </summary>
        [Category("Layout")]
        public Dimension Offset { get; set; }

        /// <summary>
        /// Gets or sets the anchor point.
        /// </summary>
        [XmlAttribute("point")]
        [Category("Layout")]
        public FramePoint Point { get; set; }

        /// <summary>
        /// Gets or sets the name of the element this anchor is relative to.
        /// </summary>
        [XmlAttribute("relativeTo")]
        [Category("Layout")]
		[EditorBrowsable]
		[ControlSelector]
        public string RelativeTo { get; set; }

        /// <summary>
        /// Gets or sets the point of the element this anchor is relative to. This property is optional.
        /// </summary>
        [XmlIgnore]
        [Category("Layout")]
        public FramePoint? RelativePoint
        {
            get { return relativePoint; }
            set { relativePoint = value; }
        }

        /// <summary>
        /// Gets or sets the point of the element this anchor is relative to.
        /// </summary>
        [XmlAttribute("relativePoint")]
        [Browsable(false)]
        public FramePoint RelativePointValue
        {
            get { return relativePoint.Value; }
            set { relativePoint = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the <see cref="RelativePointValue"/> is specified. This property is not serialized.
        /// </summary>
        [XmlIgnore]
        [Browsable(false)]
        public bool RelativePointValueSpecified
        {
            get { return relativePoint.HasValue; }
        }

        /// <summary>
        /// Creates a new object that is a copy of the instance.
        /// </summary>
        /// <returns>The copied instance.</returns>
        /// <remarks>Performs a deep copy.</remarks>
        public Anchor Copy()
        {
            return new Anchor
                       {
                           Offset = Offset != null ? Offset.Copy() : null,
                           Point = Point,
                           RelativeTo = RelativeTo,
                           RelativePoint = RelativePoint
                       };
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            var resultBuilder = new StringBuilder();

            resultBuilder.Append(Point);

            if (Offset != null)
                resultBuilder.AppendFormat(" {0};{1}", Offset.X ?? 0, Offset.Y ?? 0);

            if (RelativePoint.HasValue)
                resultBuilder.AppendFormat(" ({0})", RelativePoint);

            if(!String.IsNullOrEmpty(RelativeTo))
                resultBuilder.AppendFormat(" ({0})", RelativeTo);

            return resultBuilder.ToString();
        }

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public object Clone()
        {
            return this.Copy();
        }
    }
}